// -----------------------------
// Author: 		Dhaenens Gert	
// Creation: 	2/17/2015 7:42:23 PM				
// -----------------------------

#include "StateManager.h"

#include "Engine.h"
#include "BaseState.h"

namespace Flow
{

	StateManager::StateManager()
	{
	}

	StateManager::~StateManager()
	{
		for (auto pair : m_States)
			delete pair.second;
	}

	void StateManager::Update(Float deltaTime)
	{
		// Build the Update List
		stack<BaseState*> updateStack;
		for (auto it = m_StateStack.rbegin(); it != m_StateStack.rend(); ++it)
		{
			updateStack.push(*it);
			if ((*it)->GetUpdateOption() == StateUpdateOption::BlockUpdateBelow)
				break;
		}

		// Update the states from bottom to top
		while (!updateStack.empty())
		{
			BaseState* lpState = updateStack.top();
			updateStack.pop();

			lpState->Update(deltaTime);
		}
	}
	void StateManager::Draw(SpriteBatch* lpSpriteBatch)
	{
		// Build the Draw List
		stack<BaseState*> drawStack;
		for (auto it = m_StateStack.rbegin(); it != m_StateStack.rend(); ++it)
		{
			drawStack.push(*it);
			if ((*it)->GetDrawOption() == StateDrawOption::BlockDrawBelow)
				break;
		}

		// Draw the states from bottom to top
		while (!drawStack.empty())
		{
			BaseState* lpState = drawStack.top();
			drawStack.pop();

			lpState->Draw(lpSpriteBatch);
		}
	}

	void StateManager::AddState(BaseState* lpState)
	{
		ASSERT(lpState != nullptr);

		string name = lpState->GetName();
		ASSERT(name.length() > 0);
		ASSERT(m_States.count(name) < 1);

		m_States.insert(make_pair(name, lpState));
		lpState->OnAdd();
	}
	void StateManager::RemoveState(const string& name, Bool bDestroyState)
	{
		ASSERT(name.length() > 0);
		ASSERT(HasState(name));

		auto foundIt = m_States.find(name);
		ASSERT(foundIt != m_States.end());
		if (foundIt != m_States.end())
		{
			(*foundIt).second->OnRemove();
			m_States.erase(foundIt);
		}
	}

	void StateManager::PushState(const string& name)
	{
		ASSERT(name.length() > 0);
		ASSERT(HasState(name));

		PushCommand(new StateCommand(name, StateCommandType::Push));
	}
	void StateManager::SwapState(const string& name)
	{
		ASSERT(name.length() > 0);
		ASSERT(HasState(name));

		PushCommand(new StateCommand(name, StateCommandType::Swap));
	}
	void StateManager::PopState(const string& name, StateCommandType::Enum popType)
	{
		ASSERT(name.length() > 0);
		ASSERT(IsStateOnStack(name));
		ASSERT(popType == StateCommandType::Pop || popType == StateCommandType::PopRemove || popType == StateCommandType::PopRemoveDestroy);

		PushCommand(new StateCommand(name, popType));
	}

	BaseState* StateManager::GetState(const string& name) const
	{
		ASSERT(name.length() > 0);

		auto foundIt = m_States.find(name);
		ASSERT(foundIt != m_States.end());
		if (foundIt != m_States.end())
			return (*foundIt).second;
		return nullptr;
	}

	Bool StateManager::HasState(const string& name) const
	{
		ASSERT(name.length() > 0);

		auto foundIt = m_States.find(name);
		return (foundIt != m_States.end());
	}
	Bool StateManager::HasState(BaseState* lpState) const
	{
		ASSERT(lpState != nullptr);

		return HasState(lpState->GetName());
	}

	Bool StateManager::IsStateOnStack(const string& name) const
	{
		ASSERT(name.length() > 0);

		if (!HasState(name))
			return false;

		BaseState* lpState = GetState(name);
		return IsStateOnStack(lpState);
	}
	Bool StateManager::IsStateOnStack(BaseState* lpState) const
	{
		ASSERT(lpState != nullptr);

		auto foundIt = find(m_StateStack.begin(), m_StateStack.end(), lpState);
		return (foundIt != m_StateStack.end());
	}

	void StateManager::ProcessSingleCommand(StateCommand* lpCommand)
	{
		BaseState* lpState = GetState(lpCommand->Name);
		ASSERT(lpState != nullptr);

		// Stack Management
		switch (lpCommand->Type)
		{
		case StateCommandType::Push:
			m_StateStack.push_back(lpState);
			lpState->OnPushOrSwap();
			break;
		case StateCommandType::Swap:
			ASSERT(!m_StateStack.empty());
			if (!m_StateStack.empty())
				m_StateStack.front() = lpState;
			else
				m_StateStack.push_back(lpState);
			lpState->OnPushOrSwap();
			break;
		case StateCommandType::Pop:
		case StateCommandType::PopRemove:
		case StateCommandType::PopRemoveDestroy:
			auto foundIt = find(m_StateStack.begin(), m_StateStack.end(), lpState);
			ASSERT(foundIt != m_StateStack.end());
			if (foundIt != m_StateStack.end())
			{
				(*foundIt)->OnPop();
				m_StateStack.erase(foundIt);
			}
			break;
		}

		// Remove from the buffer
		if (lpCommand->Type == StateCommandType::PopRemove || lpCommand->Type == StateCommandType::PopRemoveDestroy)
		{
			bool shouldDestroy = (lpCommand->Type == StateCommandType::PopRemoveDestroy);
			RemoveState(lpCommand->Name, shouldDestroy);
		}
	}
}